/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.sql;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

import org.isqlviewer.sql.platform.OracleRegistrar;
import org.isqlviewer.util.LocalMessages;
import org.isqlviewer.util.LoggableObject;

/**
 * Factory class for providing mappings of fully qualified class names to specific PlatformRegistrar instances.
 * <p>
 * Instaces of this class will inspect a file called 'registrar.mappings' in the 'user.dir' of the Java runtime. This
 * file can contain extra registration handlers if needed by providing a standard Java properties file and a mapping of
 * class names of the JDBC driver to the class name of the PlatformRegistrar implementation for that Driver.
 * 
 * @author Mark A. Kobold &lt;mkobold at isqlviewer dot com&gt;
 * @version 1.0
 */
public final class RegistrarFactory extends LoggableObject {

    private static final Map<String, String> defaultMappings;

    static {
        TreeMap<String, String> mappings = new TreeMap<String, String>();
        mappings.put("oracle.jdbc.driver.OracleDriver", OracleRegistrar.class.getName());
        defaultMappings = Collections.synchronizedSortedMap(mappings);
    }

    private static final RegistrarFactory sharedInstance = new RegistrarFactory();

    public static RegistrarFactory createInstance() {

        return new RegistrarFactory();
    }

    public static RegistrarFactory getSharedInstance() {

        return sharedInstance;
    }

    private Map<String, String> classMappings = null;
    private Map<String, PlatformRegistrar> registrarCache = new TreeMap<String, PlatformRegistrar>();
    private static final String RESOURCE_BUNDLE = "org.isqlviewer.sql.ResourceBundle";
    private static final LocalMessages messages = new LocalMessages(RESOURCE_BUNDLE, JdbcService.class);

    private RegistrarFactory() {

        File mappingFile = new File(new File(System.getProperty("user.dir")), "registrar.mappings");
        if (mappingFile.exists() && mappingFile.canRead()) {
            Properties properties = new Properties();
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(mappingFile);
                properties.load(inputStream);
                classMappings = new TreeMap<String, String>();
                Set<Map.Entry<Object, Object>> set = properties.entrySet();
                for (Map.Entry<Object, Object> entry : set) {
                    classMappings.put((String) entry.getKey(), (String) entry.getValue());
                }
            } catch (IOException error) {
                debug(messages.getMessage("RegistrarFactory.failed_to_load_registrar_mappings"), error);
            }
        }
    }

    /**
     * Looks up a platform registrar by connection class name.
     * <p>
     * 
     * @param connectionClass of the JDBC platform being used.
     * @return instance of PlatformRegistrar for the driver class, <tt>null</tt> if unknown.
     */
    public PlatformRegistrar lookupRegistrar(String connectionClass) {

        PlatformRegistrar instance = registrarCache.get(connectionClass);
        if (instance == null) {
            instance = createNewInstance(classMappings, connectionClass);
            if (instance == null) {
                instance = createNewInstance(defaultMappings, connectionClass);
            }
            if (instance != null) {
                registrarCache.put(connectionClass, instance);
            }
        }
        return instance;
    }

    private PlatformRegistrar createNewInstance(Map<String, String> classMap, String connectionClass) {

        if (classMap != null) {
            String className = classMap.get(connectionClass);
            if (className != null && className.trim().length() > 0) {
                try {
                    Class< ? > clazz = Class.forName(className);
                    return (PlatformRegistrar) clazz.newInstance();
                } catch (ClassNotFoundException e) {
                    error(messages.format("RegistrarFactory.registrar_class_not_found", connectionClass));
                } catch (InstantiationException e) {
                    error(messages.format("RegistrarFactory.registrar_class_failed_to_initialize", connectionClass), e);
                } catch (IllegalAccessException e) {
                    error(messages.format("RegistrarFactory.registrar_class_not_accessible", connectionClass));
                }
            }
        }
        return null;
    }

}
